package ru.pb.game.objects.model.room;

import gnu.trove.list.array.TIntArrayList;

import org.apache.log4j.Logger;

import ru.pb.game.network.engine.client.packets.sendable.opcode_3586_ACK;
import ru.pb.game.objects.model.players.Player;
import ru.pb.game.objects.model.players.enums.SLOT_STATE;
import ru.pb.game.objects.model.players.enums.Team;
import ru.pb.game.objects.model.room.enums.SERVER_TYPE;
import ru.pb.game.objects.storage.ObjectsStorage;
import ru.pb.global.utils.Rnd;

/**
 * @author DarkSkeleton, Felixx
 * @version 1.00
 */
public class Room implements IRoom
{
	private final Logger _log = Logger.getLogger(Room.class);
	public static final int NET_ROOM_NAME_SIZE = 23;// 20;
	public static final int NET_ROOM_PW = 4;

	private static final int[] TIMES = new int[] {3, 5, 7, 5, 10, 15, 20, 25, 30};
	private static final int[] KILLS = new int[] {60, 80, 100, 120, 140, 160};
	private static final int[] ROUNDS = new int[] {0, 3, 5, 7, 9};

	public String name, password;
	public int map_id, stage4v4;
	public int allweapons, limit, seeConf, autobalans;
	public int killtime, room_type, special, random_map;

	public SERVER_TYPE server_type = SERVER_TYPE.DedicatedServer;

	public int unkc1, unkc2;

	private SLOT[] _slots = new SLOT[16];
	private int _roomId;

	private int _timeRoom;

	private int _leader;

	private boolean _isInFight = false;

	private int _redKills = 0;
	private int _redDeaths = 0;
	private int _blueKills = 0;
	private int _blueDeaths = 0;

	public int _aiCount = 1;
	public int _aiLevel = 0;

	public class SLOT
	{
		public int player;
		public SLOT_STATE state = SLOT_STATE.SLOT_STATE_EMPTY;
		public int oneTimeKills;
		public int allKills;
		public int allDeaths;
		public int gp, sp;
		public int botScore;
	}

	/**
	 * Конструктор для Room
	 * 
	 * @param _roomId
	 *            - ID Комнаты
	 */
	public Room(int roomId)
	{
		_roomId = roomId;
		for(int i = 0; i < _slots.length; i++)
			_slots[i] = new SLOT();
	}

	public int getRoomId()
	{
		return _roomId;
	}

	/**
	 * НЕ использовать при закрытии!
	 * При закрытии использовать closeSlot(int slot);
	 * 
	 * @param count
	 */
	public void initSlotCount(int count)
	{
		// Фикс туториала
		if(count == 0)
			count++;

		for(int i = 0; i < _slots.length; i++)
			if(i >= count)
				_slots[i].state = SLOT_STATE.SLOT_STATE_CLOSE;
	}

	public int getTypeServer()
	{
		return server_type.ordinal();
	}

	public void setTypeServer(SERVER_TYPE type)
	{
		server_type = type;
	}

	public int getSlotCount()
	{
		int count = 0;
		for(SLOT s : _slots)
			if(s.state != SLOT_STATE.SLOT_STATE_CLOSE)
				count++;
		return count;
	}

	public void setNewSlot(Player player, int team)
	{
		int[] slots;
		if(team == 0)
			slots = new int[] {0, 2, 4, 6, 8, 10, 12, 14};
		else
			slots = new int[] {1, 3, 5, 7, 9, 11, 13, 15};

		changeSlotState(player.getSlot(), SLOT_STATE.SLOT_STATE_EMPTY); // В старом слоте делаем пустой слот.

		for(int slot : slots)
		{
			if(_slots[slot].player == 0 && _slots[slot].state == SLOT_STATE.SLOT_STATE_EMPTY)
			{
				changeSlotState(slot, SLOT_STATE.SLOT_STATE_NORMAL);
				_slots[slot].player = player.getObjectId();
				player.setSlot(slot);
				break;
			}
		}
	}

	public SLOT_STATE getSlotState(int slot)
	{
		return _slots[slot].state;
	}

	public void changeSlotState(int slot, SLOT_STATE state)
	{
		_slots[slot].state = state;
		if(state == SLOT_STATE.SLOT_STATE_EMPTY || state == SLOT_STATE.SLOT_STATE_CLOSE)
			_slots[slot].player = 0;

		updateInfo();
	}

	public int getTimeByMask()
	{
		return TIMES[killtime >> 4];
	}

	public int getKillsByMask()
	{
		if(killtime >> 4 < 3)
			return ROUNDS[killtime & 15];
		else
			return KILLS[killtime & 15];
	}

	public void updateInfo()
	{
		for(int objId : getAllPlayers().toArray())
		{
			Player pl = ObjectsStorage.getInstance().getPlayer(objId);
			if(pl != null)
				pl.sendPacket(new opcode_3586_ACK(this));
		}
	}

	public void startBattle(int playerId)
	{
		for(int objId : getAllPlayers().toArray())
		{
			Player pl = ObjectsStorage.getInstance().getPlayer(objId);
			if(_slots[pl.getSlot()].state == SLOT_STATE.SLOT_STATE_BATTLE_READY)
				_slots[pl.getSlot()].state = SLOT_STATE.SLOT_STATE_BATTLE;
		}

		if(_leader == playerId)
		{
			_timeRoom = getTimeByMask() * 60;
			_redKills = 0;
			_redDeaths = 0;
			_blueKills = 0;
			_blueDeaths = 0;
			_log.info("Leader " + _leader + " started battle...");
		}
		// updateInfo();
	}

	public void stopBattle(int playerId)
	{
		for(int objId : getAllPlayers().toArray())
		{
			Player pl = ObjectsStorage.getInstance().getPlayer(objId);
			if(_slots[pl.getSlot()].state == SLOT_STATE.SLOT_STATE_BATTLE || _slots[pl.getSlot()].state == SLOT_STATE.SLOT_STATE_BATTLE_READY || _slots[pl.getSlot()].state == SLOT_STATE.SLOT_STATE_PRESTART || _slots[pl.getSlot()].state == SLOT_STATE.SLOT_STATE_LOAD|| _slots[pl.getSlot()].state == SLOT_STATE.SLOT_STATE_RENDEZVOUS)
				_slots[pl.getSlot()].state = SLOT_STATE.SLOT_STATE_NORMAL;
		}

		if(_leader == playerId)
		{
			_log.info("Leader " + _leader + " end battle");
			_aiLevel = 1;
			_redKills = 0;
			_redDeaths = 0;
			_blueKills = 0;
			_blueDeaths = 0;
			_timeRoom = getTimeByMask() * 60;
			// updateInfo();
		}
	}

	public void setTimeLost(int time)
	{
		_timeRoom = time;
	}

	public int getTimeLost()
	{
		return _timeRoom;
	}

	/**
	 * Устанавливает лидера комнаты.
	 * 
	 * @param p
	 */
	public void setLeader(int objId)
	{
		_leader = objId;
	}

	/**
	 * Возвращает лидера комнаты. Если лидера нет, выбирается рандомный игрок из списка игроков комнаты.
	 * 
	 * @return - Player
	 */
	public Player getLeader()
	{
		if(_leader == 0)
			setNewLeader(0);
		return ObjectsStorage.getInstance().getPlayer(_leader);
	}

	/**
	 * Устанавливает нового лидера комнаты
	 * 
	 * @param - int
	 */
	public void setNewLeader(int newLeader)
	{
		if(newLeader == 0)
			setLeader(Rnd.get(getAllPlayers().toArray()));
		else
			setLeader(newLeader);
	}

	/**
	 * Установка статуса "в бою"/"ожидание"
	 * 
	 * @param b
	 */
	public void setInFight(boolean b)
	{
		_isInFight = b;
	}

	/**
	 * Проверка статуса комнаты на начало боя.
	 * 
	 * @return - В бою или нет
	 */
	public boolean isInFight()
	{
		return _isInFight;
	}

	/**
	 * Добавляет Игрока в список игроков комнаты.
	 * 
	 * @param p
	 *            - Player
	 */
	public void addPlayer(Player player)
	{
		for(int i = 0; i < _slots.length; i++)
		{
			if(_slots[i].player == 0 && _slots[i].state != SLOT_STATE.SLOT_STATE_CLOSE)
			{
				_slots[i].player = player.getObjectId();
				_slots[i].state = SLOT_STATE.SLOT_STATE_NORMAL;
				player.setRoom(this);
				player.setSlot(i);
				break;
			}
		}
		updateInfo();
	}

	/**
	 * Удаление Игрока из списка игроков комнаты по object_id
	 * 
	 * @param p
	 *            - Player
	 */
	public void removePlayer(Player player)
	{
		for(int i = 0; i < _slots.length; i++)
		{
			if(_slots[i].player != 0 && _slots[i].player == player.getObjectId())
			{
				_slots[i].player = 0;
				_slots[i].botScore = 0;
				player.setRoom(null);
				player.setSlot( -1);
				break;
			}
		}
		updateInfo();
	}

	public SLOT[] getSlots()
	{
		return _slots;
	}

	public SLOT getSlot(int id)
	{
		return _slots[id];
	}

	public TIntArrayList getPlayersByTeam(Team t)
	{
		TIntArrayList temp = new TIntArrayList();
		for(int i = 0; i < _slots.length; i++)
			if(_slots[i].player != 0 && ObjectsStorage.getInstance().getPlayer(_slots[i].player).getTeam() == t)
				temp.add(_slots[i].player);
		return temp;
	}

	public TIntArrayList getAllPlayers()
	{
		TIntArrayList temp = new TIntArrayList();
		for(int i = 0; i < _slots.length; i++)
			if(_slots[i].player != 0)
				temp.add(_slots[i].player);
		return temp;
	}

	public Player getPlayerBySlot(int slot)
	{
		return ObjectsStorage.getInstance().getPlayer(_slots[slot].player);
	}

	public void addDeaths(Team t)
	{
		addDeaths(t, 1);
	}

	public void addDeaths(Team t, int value)
	{
		switch(t)
		{
			case CHARACTER_TEAM_RED:
			{
				_redDeaths += value;
				break;
			}
			case CHARACTER_TEAM_BLUE:
			{
				_blueDeaths += value;
				break;
			}
		}
	}

	public int getDeaths(Team t)
	{
		switch(t)
		{
			case CHARACTER_TEAM_RED:
			{
				return _redDeaths;
			}
			case CHARACTER_TEAM_BLUE:
			{
				return _blueDeaths;
			}
			default:
			{
				return 0;
			}
		}
	}

	public void addKills(Team t)
	{
		addKills(t, 1);
	}

	public void addKills(Team t, int value)
	{
		switch(t)
		{
			case CHARACTER_TEAM_RED:
			{
				_redKills += value;
				break;
			}
			case CHARACTER_TEAM_BLUE:
			{
				_blueKills += value;
				break;
			}
		}
	}

	public int getKills(Team t)
	{
		switch(t)
		{
			case CHARACTER_TEAM_RED:
			{
				return _redKills;
			}
			case CHARACTER_TEAM_BLUE:
			{
				return _blueKills;
			}
			default:
			{
				return 0;
			}
		}
	}
}